EventBus初理解

缘由
平时工作,因为懒于动笔的原因,也没注重技术和经验的积累,导致之前曾经研究过的问题现在又忘记了,所以要慢慢注重积累,那么就从写作开始,谈谈对工作中碰到的问题进行整理和归纳。
我们都知道,在Android中,想处理事件传递,可以用Handler+MessageQueue+Message+Looper循环,固然是有解决方法,但是这个使用起来不方便,代码写起来也不简洁,同时还必须要理解好Handler+MessageQueue+Message+Looper之间的关系,比如这样的图:

331079-20160201183503929-867948336.png

初认识:

因为在工作大量用到Fragment和Activity之间的耦合,需要获取对方某某的实例,自然而然就去寻找事件之间是否有解耦合的库,EventBus就涌现出来了,这个是greenrobot大神写的,多么经典已经无需多说了,github地址:https://github.com/greenrobot/EventBus,虽然我们会使用,但是也需要了解里面的原理,这样使用的起来也放心,毕竟对于陌生的东西不明白内部情况,就去用,什么时候被坑也会不知道的。

脑补下:
介绍这个库之前,需要认识以下几个名词。
事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属的 Class。事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件。
订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 onEvent 函数,这个函数叫事件响应函数。订阅者通过 register 接口订阅某个事件类型,unregister 接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为 0。
发布者(Publisher):发布某事件的对象,通过 post 接口发布事件。

结构图:

331079-20160201183554413-608748702.png

从中,我们可以看到这个库整体结构是基于生产者/消费者模式,也可以称呼为发布者/订阅者,显得更亲切。所谓的发布者/订阅者,比如我们日常生活中,小区的通告栏,生产者类似小区居委会,订阅者就是小区里的住户,当你需要小区居委会帮忙的时候,比如你外出了,可以叫居委会里某个大妈帮你拿下快递,那么你需要在居委会那里登记,也可以叫做注册,只有登记了,居委会里的大妈才知道你是我们同一个小区的,也可以认识你了。同时,每当有事情发生,需要通知的时候,需要在通告栏里发布一个通告,现在信息发达了,通告栏可以通过微信公众号来发布,但是也需要每个人去扫一扫,关注下小区的公众号,一发通知,有关注的业主自然就知道通知事情是什么。那么这个EventBus库的实现原理,跟我们日常生活中的微信公众号很类型,首先在使用的时候,我们需要向公众平台注册,注册完之后, 当生产者需要发布信息的时候,平台会帮我们把这些消息推送给订阅者,订阅者根据消息内容,进行不同处理操作。

使用方法流程:


331079-20160201183828866-745219582.jpg

类图:


331079-20160201183605116-368820432.png

根据类来分析,从中,我们可以看到EventBus依赖有五个类,分别如下。
SubscriberMethod:这是一个订阅方法类的封装,包含了方法反射的名称,线程模型和事件类型,比如当你向主干注册的时候,这时候就会实例化SubscriberMethod类实例,来存放以onEvent开头的方法,运行的线程模型和自定义的传递事件类型。

SubscriberMethodFinder:在这个类中,我们可以看到为何在定义方法消息接受回调时,会以“onEvent”开头的方法,因为这里有个

private static final String ON_EVENT_METHOD_NAME = "onEvent";

ON_EVENT_METHOD_NAME的常量。里面关键方法是findSubscriberMethods(),具体实现可以去看代码,通过反射的方法,找出订阅者上的以onEvent开头的方法,最终返回的时候SubscriberMethod类的集合,也就是所有事件响应函数。
HandlerPoster:这是继承Handler的请求类,封装了请求队列,整个过程是在队列不断发送请求,直到所有的请求都出队列,也就是全部发送完毕。事件主线程处理,对应ThreadMode.MainThread。enqueue 函数将事件放到队列中,并利用 handler 发送 message,handleMessage 函数从队列中取事件,invoke 事件响应函数处理。
AsyncPoster:这个其实一个Runnable实现类,封装执行的过程,在异步时调用。是一个事件异步线程处理,对应ThreadMode.Async。enqueue 函数将事件放到队列中,并调用线程池执行当前任务,在 run 函数从队列中取事件,invoke 事件响应函数处理。
BackgroundPoster:这个其实一个Runnable实现类,事件 Background 处理,对应ThreadMode.BackgroundThread。enqueue 函数将事件放到队列中,并调用线程池执行当前任务,在 run 函数从队列中取事件,invoke 事件响应函数处理。与 AsyncPoster.java 不同的是BackgroundPoster 中的任务只在同一个线程中依次执行,而不是并发执行。

ThreadMode模型
线程模型共有四类:
•PostThread,默认的ThreadMode,表示在执行Post操作的线程直接调用订阅者的事件响应方法不论该线程是否为主线程(UI线程)。当该线程为主线程时,响应方法不能有耗时操作,否则有卡主线程风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作。
•MainThread,在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;

•BackgroundThread,在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
•Async,不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。

EventBus类介绍
EventBus类是对外暴露的API,包括register(),post(),unregister()方法,配合自定义的EventTypeji s事件响应,即可完成使用过程。
EventBus类有个默认getDefault单例,当然也可以通过EventBusBuilder 或构造函数新建一个EventBus,每个新建的EventBus发布和订阅事件都是相互隔离的,即在一个EventBus对象中发布事件,如果有另外一个EventBus对象,则另外一个EventBus对象的订阅者不会收到该订阅。
关键点:
1,register和unregister,分别表示订阅事件和取消订阅,register 最底层函数有三个参数,分别为订阅者对象、是否是 Sticky 事件、优先级。

331079-20160201184346210-446053082.png

注册流程:
331079-20160201183938022-648821093.png

register 函数中会先根据订阅者类名去subscriberMethodFinder中查找当前订阅者所有事件响应函数,然后循环每一个事件响应函数,依次执行下面的 subscribe 函数:
第一,通过subscriptionsByEventType得到该事件类型的所有订阅者信息队列,根据优先级把当前订阅者信息插入到订阅者队列subscriptionsByEventType中。
第二,在typesBySubscriber中得到当前订阅者的所有事件队列,将此事件保存到队列typesBySubscriber中,用于后续取消订阅。
第三,检查这个事件是否是 Sticky 事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。

发布流程:


331079-20160201183953882-1202721136.png

post方法,用于发布事件,cancle方法用户取消订阅者订阅的所有事件类型。
post 方法会首先得到当前线程的 post 信息PostingThreadState,其中包含事件队列,将当前事件添加到其事件队列中,然后循环调用 postSingleEvent 函数发布队列中的每个事件。
postSingleEvent 方法会先去eventTypesCache得到该事件对应类型的的父类及接口类型,没有缓存则查找并插入缓存。循环得到的每个类型和接口,调用 postSingleEventForEventType 方法发布每个事件到每个订阅者。
postSingleEventForEventType 方法在subscriptionsByEventType查找该事件订阅者订阅者队列,调用 postToSubscription 函数向每个订阅者发布事件。

结尾

重点名称解释:
•typesBySubscriber订阅者订阅的事件的保存队列,以 subscriber 为 key,元素为 eventType 的 ArrayList 为 Value。
•currentPostingThreadState当前线程的 post 信息,包括事件队列、是否正在分发中、是否在主线程、订阅者信息、事件实例、是否取消。
•mainThreadPoster、backgroundPoster、asyncPoster事件主线程处理者、事件 Background 处理者、事件异步处理者。
•subscriberMethodFinder订阅者响应函数信息存储和查找类。
•executorService异步和 BackGround 处理方式的线程池。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容